import base64
import json
import pickle

from django_redis import get_redis_connection
from rest_framework.response import Response
from rest_framework.views import APIView

import constants
from carts.serializers import CartSerializer, CartSKUSerializer, CartSelectSerializer
from goods.models import SKU


class CartView(APIView):

	def perform_authentication(self, request):
		"""
	 	由于前端在传回参数的时候，请求头 Authorization 中包含了 JWT Token，REST Framework会自动进行验证，如果验证不成功会返回404
		Perform authentication on the incoming request.
		Note that if you override this and simply 'pass', then authentication
		will instead be performed lazily, the first time either
		`request.user` or `request.auth` is accessed.
		"""
		pass

	def post(self, request):
		"""
		增加购物车请求，结果保存在redis中
		:param request: request会传回 sku_id , count , selected 三个参数
		:return:
		"""
		# 1、通过序列化器获取三个参数
		sku_serializer = CartSerializer(data=request.data)
		sku_serializer.is_valid()
		sku_id = sku_serializer.validated_data.get('sku_id')
		count = sku_serializer.validated_data.get('count')
		selected = sku_serializer.validated_data.get('selected')

		# 2、判断是否是登陆用户
		try:
			user = request.user
		except Exception as e:
			user = None

		# 3、如果是登陆用户，把数据定入Redis
		if user is not None and user.is_authenticated:
			redis = get_redis_connection('cart').pipeline()
			redis.hincrby('cart_%s' % user.id, sku_id, count)
			if selected:
				redis.sadd('cart_selected_%s' % user.id, sku_id)
			redis.execute()
			return Response(sku_serializer.data, status=201)

		# 4、如果未登陆，重新生成Response后，写入cookie返回
		else:
			cookie = request.COOKIES.get('cart')

			try:
				skus_get = pickle.loads(base64.b64decode(cookie))
			except:
				skus_get = {}

			if sku_id in skus_get:
				cookie_count = skus_get[sku_id].get('count', 0) + count
			else:
				cookie_count = count

			skus_get[sku_id] = {'count': cookie_count, 'selected': selected}

			response = Response(sku_serializer.data, status=201)
			cookie_content = base64.b64encode(pickle.dumps(skus_get)).decode()
			response.set_cookie('cart', cookie_content, max_age=constants.CART_COOKIE_EXPIRES)

			return response

	def get(self, request):
		"""
		返回购物车信息
		:param request:
		:return:
		"""

		try:
			user = request.user
		except Exception as e:
			user = None

		skus_get = {}
		if user is not None and user.is_authenticated:
			# pipeline不能直接赋值
			redis = get_redis_connection('cart')
			counters = redis.hgetall('cart_%s' % user.id)
			sets = redis.smembers('cart_selected_%s' % user.id)

			for sku,count in counters.items():
				sku_id = int(sku)
				count = int(count)
				skus_get[sku_id] = {'count':count, 'selected':sku in sets}
		else:
			cookie = request.COOKIES.get('cart')
			try:
				skus_get = pickle.loads(base64.b64decode(cookie))
			except Exception as e:
				pass

		skus = SKU.objects.filter(id__in=skus_get.keys())
		for sku in skus:
			id = sku.id
			sku.count = skus_get[id].get('count',0)
			sku.selected = skus_get[id].get('selected',True)

		sku_serializer = CartSKUSerializer(skus, many=True)

		return Response(sku_serializer.data, status=201)


	def put(self, request):

		serialize = CartSerializer(data=request.data)
		serialize.is_valid()

		sku_id = serialize.validated_data.get('sku_id')
		count = serialize.validated_data.get('count')
		selected = serialize.validated_data.get('selected')

		try:
			user = request.user
		except Exception as e:
			user = None

		if user is not None and user.is_authenticated:
			redis = get_redis_connection('cart').pipeline()
			if count <=0 :
				redis.hdel('cart_%s' % user.id, sku_id)
				redis.srem('cart_selected_%s' % user.id, sku_id)
			else:
				redis.hset('cart_%s' % user.id, sku_id, count)

				if selected:
					redis.sadd('cart_selected_%s' % user.id, sku_id)
				else:
					redis.srem('cart_selected_%s' % user.id, sku_id)

			redis.execute()

			response = Response(serialize.data, status=200)
		else:
			cookie = request.COOKIES.get('cart')
			skus_get = pickle.loads(base64.b64decode(cookie))       # type:dict
			if count <= 0:
				skus_get.pop(sku_id)
			else:
				skus_get[sku_id]['count'] = count
				skus_get[sku_id]['selected'] = selected

			response = Response(serialize.data, status=200)
			cookie_content = base64.b64encode(pickle.dumps(skus_get)).decode()
			response.set_cookie('cart', cookie_content, constants.CART_COOKIE_EXPIRES)

		return response


	def delete(self, request):

		sku_id = request.data.get('sku_id')

		try:
			sku = SKU.objects.get(id=sku_id)
		except Exception as e:
			raise Response({'message':'sku_id没有对应的商品信息'}, status=402)

		try:
			user = request.user
		except Exception as e:
			user = None

		if user is not None and user.is_authenticated:
			redis = get_redis_connection('cart').pipeline()
			redis.hdel('cart_%s' % user.id, sku_id)
			redis.srem('cart_selected_%s' % user.id, sku_id)
			redis.execute()
			response = Response({'message':'ok'}, status=200)

		else:
			cookie = request.COOKIES.get('cart')
			skus_get = pickle.loads(base64.b64decode(cookie))
			# print(type(skus_get),skus_get)
			del skus_get[sku_id]

			response = Response({'message':'ok'}, status=200)
			cookie_content = base64.b64encode(pickle.dumps(skus_get)).decode()
			response.set_cookie('cart', cookie_content, constants.CART_COOKIE_EXPIRES)

		return response


class CartSelectView(APIView):

	def perform_authentication(self, request):
		pass

	def put(self, request):

		serializer = CartSelectSerializer(data=request.data)
		serializer.is_valid()
		selected = serializer.validated_data.get('selected')

		try:
			user = request.user
		except Exception as e:
			user = None

		if user and user.is_authenticated:
			redis = get_redis_connection('cart')
			keys = redis.hkeys('cart_%s' % user.id)

			if selected:
				redis.sadd('cart_selected_%s' % user.id, *keys)
			else:
				redis.srem('cart_selected_%s' % user.id, *keys)

			response = Response({'message':'ok'})

		else:
			cookie = request.COOKIES.get('cart')
			skus_get = pickle.loads(base64.b64decode(cookie))

			for sku in skus_get.values():
				sku['selected'] = selected

			response = Response({'message':'ok'})

			cookie_content = base64.b64encode(pickle.dumps(skus_get)).decode()
			response.set_cookie('cart', cookie_content, constants.CART_COOKIE_EXPIRES)

		return response
